﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace StructConver
{
    public static class ReadMsg
    {    
        
        /// <summary>
         /// 整个字符串的stringbuilder
         /// </summary>
        static StringBuilder sbd = new StringBuilder();
        /// <summary>
        /// 某个类中所有的结构体类型
        /// </summary>
        static List<Type> classAllTypes = new List<Type>();
        /// <summary>
        /// 也解析完成的type集合
        /// </summary>
        static List<Type> listOkType = new List<Type>();
        /// <summary>
        /// 保存结构体长度的数组
        /// </summary>
        static Dictionary<string, int> mapTypeSize = new Dictionary<string, int>();
        static List<String> listOkNames = new List<string>();
        static string newClassName;
        /// <summary>
        /// 把bytes转化为结构体对象
        /// </summary>
        /// <param name="t"></param>
        public static void CreateClassBytesToStruct(Type t)
        {
            sbd.Clear();
            newClassName = t.Name+"_Read.";
            classAllTypes.AddRange(t.GetNestedTypes());
            //listOkType.Clear();
            //Console.WriteLine("Type length=" + classAllTypes.Length);
            //构建class代码;
            sbd.AppendLine("using System;");
            sbd.AppendLine(string.Format("using static {0};", t.Name));
            sbd.AppendLine(string.Format("public class {0}_{1} ", t.Name, "Read"));
            sbd.AppendLine("{");
            foreach (Type classType in classAllTypes)
            {
                if (classType.BaseType == typeof(System.Enum))
                {
                    continue;
                }
                if (!listOkType.Contains(classType))
                {
                    string strStruct = FindStructField(classType);
                    sbd.Append(strStruct);
                }
            }
            sbd.AppendLine("}");
            string path = CreateCSFile(sbd.ToString(), t.Name + "_Read");
            Console.WriteLine("文件输出路径="+path);
        }

        /// <summary>
        /// 查找结构体中所有字段
        /// </summary>
        /// <param name="typeStruct"></param>
        /// <returns></returns>
        static string FindStructField(Type typeStruct)
        {
            FieldInfo[] fls = typeStruct.GetFields();
            int structPack = FindStructPack(typeStruct);
            string covStr = null;
            string structName = typeStruct.Name;
 
            int fieldSize = 0;
            int SizeConst = 0;
            int index = 0;
            int indexAdd = 0;
            double buMin = 0;
            bool isStruct = false;
            bool isArray = false;
             
            //存入结构体大小的数组;
            mapTypeSize.Add(structName, Marshal.SizeOf(typeStruct));
            StringBuilder sbd2 = new StringBuilder();
            //过滤内嵌结构体;
            FindInternalStruct(fls);
            //处理字段属性;
            if (fls.Length > 0)
            {
                //构建结构体代码;
                sbd2.AppendLine(string.Format("\t public static {0} Read_{1}(byte[] bytes) ", structName, structName));
                sbd2.AppendLine("\t {");
                sbd2.AppendLine(string.Format("\t\t // Marshal.SizeOf({0})={1},Pack={2}", structName, mapTypeSize[structName], structPack));
                sbd2.AppendLine(string.Format("\t\t {0} obj = new {1}();", structName, structName));
                sbd2.AppendLine("\t\t int SizeConst = 0; ");
                sbd2.AppendLine("\t\t int index = 0; ");
                foreach (FieldInfo fl in fls)
                {
                    SizeConst = FindFieldSizeConst(fl);
                    isStruct = CheckIsStruct(fl.FieldType);
                    isArray = fl.FieldType.IsArray;
                    if (SizeConst > 1)
                    {   //这个字段是数组 需要查找到他的基础长度;
                        fieldSize = FindArrayBaseField(fl);
                        GetValueTypeSize(fl.FieldType.ToString(), out covStr);
                        sbd2.AppendLine(string.Format("\t\t SizeConst = {0};", SizeConst));
                        
                    }
                    else
                    {   //这个字段是普通值类型;
                        fieldSize = GetValueTypeSize(fl.FieldType.ToString(), out covStr);
                    }

                    // pack指定的数值和结构(或联合)最大数据成员长度中，比较小的那个进行;
                    buMin = fieldSize <= structPack ? fieldSize : structPack;
                    if (index != 0 && index % buMin != 0 && !isStruct)
                    {
                        index = (int)Math.Ceiling((index / buMin)) * fieldSize;
                    }
                    if (SizeConst>1)
                    {
                        sbd2.AppendLine(string.Format("\t\t index = {0};", index));
                    }

                    if (isStruct)
                    {   //判断数组 创建一套读取结构体的代码; 结构体中也修改index下标值;
                        string str = isArray ? CreateStructArrayRead(fl, ref index, SizeConst) : CreateStructRead(fl, ref index, SizeConst);
                        sbd2.AppendLine(str);
                    }
                    else
                    {
                        //判断数组
                        if (isArray)
                        {
                            string tBase = FindArrayBaseType(fl.FieldType).Name;
                            string head = CheckIsCreateValueArrayRead(newClassName, tBase);
                            if (!listOkType.Contains(fl.FieldType))
                            {
                                string strBaseType = CreateValueArrayRead(fl, ref index, SizeConst);
                                sbd.Append(strBaseType);
                                listOkType.Add(fl.FieldType);
                            }
                            covStr = (string.Format(" {0}(bytes, {1}, SizeConst);", head, index));
                        }
                        else
                        {
                            covStr =   string.Format(covStr, index);
                        }
                        //判断数组 创建读取一个字段的代码;
                        sbd2.AppendLine(string.Format("\t\t obj.{0}={1}", fl.Name, covStr));
                        //判断是否数组;
                        indexAdd = isArray ? fieldSize * SizeConst : fieldSize;
                        //本次增量长度;
                        index += indexAdd;
                    }
                    isStruct = false;
                    SizeConst = 0;
                    fieldSize = 0;
                }
                sbd2.AppendLine("\t\t return obj;");
                sbd2.AppendLine("\t }");
                CheckIsCreateStructRead(newClassName, structName);
            }
            listOkType.Add(typeStruct);
            return sbd2.ToString();
        }

        /// <summary>
        /// 判断是否是结构体类型
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        static bool CheckIsStruct(Type t)
        {
            string typeName = t.Name.Replace("[]", "").Trim();
            foreach (Type ty in classAllTypes)
            {
                if (typeName == ty.Name)
                {   //这是一个结构体;
                    return true;
                }
            }
            return false;
        }


        /// <summary>
        /// 检查是否创建过这个结构体读取函数
        /// </summary>
        /// <param name="className"></param>
        /// <param name="structName"></param>
        /// <returns></returns>
        static string CheckIsCreateStructRead(string className, string structName)
        {
            string head = "Read_" + structName;
            foreach (string tmpHead in listOkNames)
            {
                if (tmpHead.Contains(head))
                {   //存在过 直接return;
                    return tmpHead.Replace(className, string.Empty);
                }
            }
            //首次存入 add;
            listOkNames.Add(className + head);
            return head;
        }

        /// <summary>
        /// 检查是否创建过这个函数了 获取他的名称
        /// </summary>
        /// <param name="typeName"></param>
        /// <returns></returns>
        static string CheckIsCreateValueArrayRead(string className, string typeName)
        {
            string head = string.Format("Read_{0}Array", typeName);
            foreach (string tmpHead in listOkNames)
            {
                if (tmpHead.Contains("."+head))
                {   //存在过 直接return;
                    return tmpHead.Replace(className, string.Empty); ;
                }
            }
            //首次存入 add;
            listOkNames.Add(className+ head);
            
            return head;
        }

        /// <summary>
        /// 创建基本类型数组的读取
        /// </summary>
        /// <param name="fl"></param>
        /// <param name="index"></param>
        /// <param name="SizeConst"></param>
        /// <returns></returns>
        static string CreateValueArrayRead(FieldInfo fl, ref int index, int SizeConst)
        {
            Type tp = fl.FieldType.IsArray ? FindArrayBaseType(fl.FieldType) : fl.FieldType;
            string covStr;
            int size = GetValueTypeSize(tp.ToString(), out covStr);
            string tmp = null;
            /////////////////////////////////
            tmp += string.Format("\t public static {0}[] Read_{1}Array(byte[] bytes, int index , int SizeConst)\n",tp.Name, tp.Name);
            tmp += "\t {\n";
            //tmp += string.Format("\n\t\t int SizeConst = {0};\n", SizeConst);
            //tmp += string.Format("\t\t int index = {0};\n", 0);
            tmp += string.Format("\t\t {0}[] arr = new {1}[SizeConst];\n", tp.Name, tp.Name);
            tmp += string.Format("\t\t for (int i = 0;i<SizeConst; i++)\n");
            tmp += "\t\t {\n";
            tmp += string.Format("\t\t\t arr[i] = {0}\n", string.Format(covStr, "index"));
            tmp += string.Format("\t\t\t index = index + {0};\n", size);
            tmp += "\t\t }\n";
            tmp += ("\t\t return arr;\n");
            tmp += ("\t }\n");
            ////////////////////////////////
            return tmp;
        }

        /// <summary>
        /// 创建结构体数组的读取
        /// </summary>
        /// <param name="fl"></param>
        /// <param name="index"></param>
        /// <param name="SizeConst"></param>
        /// <returns></returns>
        static string CreateStructArrayRead(FieldInfo fl, ref int index, int SizeConst)
        {
            Type tp = fl.FieldType.IsArray ? FindArrayBaseType(fl.FieldType) : fl.FieldType;
            int size = GetStructTypeSize(tp);
            string tmp = null;
            string typeName = tp.Name;
            string head = CheckIsCreateStructRead(newClassName, tp.Name);
            if (!head.Contains(newClassName))
            {
                typeName = tp.FullName.Replace("+",".");
            }
            /////////////////////////////////
            //tmp += string.Format("\n\t\t int SizeConst = {0};\n", SizeConst);
            //tmp += string.Format("\t\t int index = {0};\n", index);
            tmp += string.Format("\t\t {0}[] arr_{1} = new {2}[SizeConst];\n", typeName, fl.Name, typeName);
            tmp += string.Format("\t\t byte[] bts_{0} = new byte[{1}];\n", fl.Name, size);
            tmp += string.Format("\t\t for (int i = 0;i<SizeConst; i++)\n");
            tmp += "\t\t {\n";
            tmp += string.Format("\t\t\t Array.Copy(bytes, index, bts_{0}, 0, {1});\n", fl.Name, size);
            tmp += string.Format("\t\t\t arr_{0}[i] = {1}(bts_{2});\n", fl.Name, head, fl.Name);
            tmp += string.Format("\t\t\t index = index + {0};\n", size);
            tmp += "\t\t }\n";
            tmp += string.Format("\t\t obj.{0} =arr_{1};\n", fl.Name, fl.Name);
            ////////////////////////////////
            return tmp;
        }

        /// <summary>
        /// 生成一个结构体的读取方式
        /// </summary>
        /// <param name="tp"></param>
        /// <returns></returns>
        static string CreateStructRead(FieldInfo fl, ref int index, int SizeConst = 1)
        {
            Type tp = fl.FieldType.IsArray ? FindArrayBaseType(fl.FieldType) : fl.FieldType;
            int size = GetStructTypeSize(tp) * SizeConst;
            string tmp = null;
            //判断index是否是这个结构体中最大基础类型
            int fieldMin = 0;
            int fieldMax = 0;
            int packSize = FindStructPack(tp);
            //查找这个结构体 最短最长的字段;
            FindStructMinMaxField(tp.GetFields(), out fieldMin, out fieldMax);
            if (index % fieldMax != 0)
            {   //计算下标;
                index = (int)(Math.Ceiling((index * 1.0f / fieldMax))) * fieldMax;
            }
            string bytesName = "bytes_" + fl.Name;
            tmp += string.Format("\n\t\t byte[] {0} = new byte[{1}];\n", bytesName, size);
            tmp += string.Format("\t\t Array.Copy(bytes, {0}, {1} , 0, {2});\n", index, bytesName, size);
            string head = CheckIsCreateStructRead(newClassName, tp.Name);
            tmp +=string.Format("\t\t obj.{0}= {1}({2});", fl.Name, head, bytesName) ;
            index = index + size;
            //验证整体对齐系数 = min((max(int,double,float), 8) = 8，将内存大小由28补齐到8的整数倍32
            int r = Math.Min(packSize, fieldMax);
            if (index % r != 0)
            {
                index = (int)(Math.Ceiling((index * 1.0f / r))) * r;
            }

            return tmp;
        }

        /// <summary>
        /// 查找结构体中字段的最小 最大长度
        /// </summary>
        /// <param name="fls"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        static void FindStructMinMaxField(FieldInfo[] fls, out int min, out int max)
        {
            min = 0;
            max = 0;
            int sizeField = 0;
            string tmp = null;
            foreach (FieldInfo fl in fls)
            {
                bool isStruct = CheckIsStruct(fl.FieldType);
                if (isStruct)
                {
                    //continue;
                }
                Type type = fl.FieldType.IsArray ? FindArrayBaseType(fl.FieldType) : fl.FieldType;
                sizeField = GetValueTypeSize(type.ToString() , out tmp);
                if (sizeField==0 && isStruct && mapTypeSize.ContainsKey(type.Name))
                {
                    sizeField = mapTypeSize[type.Name];
                }
                if (min == 0 && max == 0 && sizeField != 0)
                {
                    min = sizeField;
                    max = sizeField;
                }
                if (sizeField != 0)
                {
                    min = sizeField <= min ? sizeField : min;
                    max = sizeField < max ? max : sizeField;
                }

            }
        }

        /// <summary>
        /// 查找结构体中数组字段的基础类型长度
        /// </summary>
        /// <param name="fls"></param>
        static int FindArrayBaseField(FieldInfo fl)
        {
            int size = 0;
            string outStr = null;

            Type tp = fl.FieldType;
            if (fl.FieldType.BaseType.Name.Equals("Array"))
            {
                tp = FindArrayBaseType(fl.FieldType);
            }
            size = GetValueTypeSize(tp.ToString(), out outStr);

            return size;
        }

        /// <summary>
        /// 获得数组的基础类型
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        static Type FindArrayBaseType(Type t)
        {
            //if (!t.IsArray)
            //    return null;
            string tName = t.FullName.Replace("[]", string.Empty);
            Type elType = t.Assembly.GetType(tName);
            return elType;
        }


        /// <summary>
        /// 查找结构体设置的Pack对齐长度
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        static int FindStructPack(Type type)
        {
            StructLayoutAttribute m = type.StructLayoutAttribute;
            return m.Pack;
        }

        /// <summary>
        /// 查找这个字段的数组长度
        /// </summary>
        /// <param name="field"></param>
        /// <returns></returns>
        static int FindFieldSizeConst(FieldInfo field)
        {
            foreach (var m in field.CustomAttributes)
            {
                foreach (var n in m.NamedArguments)
                {
                    if (n.MemberName == "SizeConst")
                    {
                        return (int)n.TypedValue.Value;
                    }
                }
            }
            return 1;
        }



        /// <summary>
        /// 查找内部嵌套的结构体
        /// </summary>
        /// <param name="type"></param>
        static void FindInternalStruct(FieldInfo[] fls)
        {
            string tmp;
            int size = 0;
            foreach (FieldInfo fl in fls)
            {
                size = GetValueTypeSize(fl.FieldType.ToString(), out tmp);
                if (size == 0)
                {   //不属于基本类型;
                    foreach (Type ty in classAllTypes)
                    {
                        string typeName = fl.FieldType.Name.Replace("[]", "").Trim();
                        if (typeName == ty.Name)
                        {
                            //Console.WriteLine("发现需要处理的结构体=" + ty.Name);
                            if (!listOkType.Contains(ty))
                            {
                                string strStruct = FindStructField(ty);
                                sbd.Append(strStruct);
                            }

                        }
                    }
                }
            }
        }

        /// <summary>
        /// 返回这个结构体的总长度
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        static int GetStructTypeSize(Type type)
        {
            return Marshal.SizeOf(FindArrayBaseType(type));
        }

        /// <summary>
        /// 获得常见类型的字节长度
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        static int GetValueTypeSize(string type, out string bitCov)
        {
            switch (type)
            {   //返回字节数;
                case "System.Int16":
                    bitCov = "BitConverter.ToInt16(bytes , {0});";
                    return 2;
                case "System.Int32":
                    bitCov = "BitConverter.ToInt32(bytes , {0});";
                    return 4;
                case "System.Int64":
                    bitCov = "BitConverter.ToInt64(bytes , {0});";
                    return 8;
                case "System.UInt16":
                    bitCov = "BitConverter.ToUInt16(bytes , {0});";
                    return 2;
                case "System.UInt32":
                    bitCov = "BitConverter.ToUInt32(bytes , {0});";
                    return 4;
                case "System.UInt64":
                    bitCov = "BitConverter.ToUInt64(bytes , {0});";
                    return 8;
                case "System.Boolean":
                    //服务端bool只有一个字节, 而BOOL长度视实际环境来定，一般可认为是4个字节
                    bitCov = "BitConverter.ToInt32(bytes , {0})>0;";//BOOL4个字节
                    //bitCov = "BitConverter.ToBoolean(bytes , {0});";//bool1个字节
                    return 4;
                case "System.Byte":
                    bitCov = "bytes[{0}];";
                    return 1;
                case "System.SByte":
                    bitCov = "bytes[{0}];";
                    return 1;
                case "System.Char":
                    bitCov = "BitConverter.ToChar(bytes , {0});";
                    return 2;
                case "System.Single":
                    bitCov = "BitConverter.ToSingle(bytes , {0});";
                    return 4;
                case "System.Double":
                    bitCov = "BitConverter.ToDouble(bytes , {0});";
                    return 8;
                default:
                    //Console.WriteLine("*********** 发现未知类型= "+type);
                    bitCov = "Read_Error";
                    return 0;
            }
        }


        static string CreateCSFile(string content, string fileName)
        {
            string pathRoot = System.Environment.CurrentDirectory;
            string path = pathRoot +"\\"+ fileName + ".cs";
            Console.WriteLine(content);
            using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                using (TextWriter textWriter = new StreamWriter(fileStream, Encoding.UTF8))
                {
                    textWriter.Write(content);
                }
            }
            return path;
        }

    }
}
